package com.facebook.swift.codec.metadata;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

import net.gdface.utils.ClassCommentProvider;

import static com.facebook.swift.codec.metadata.Decorators.javadocCommentProviderFactory;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * {@link ThriftEnumMetadata}的代理类<br>
 * 重载所有{@link ThriftFieldMetadata}相关方法
 * @author guyadong
 *
 */
@Immutable
public class DecoratorThriftEnumMetadata<T extends Enum<T>> extends ThriftEnumMetadata<T> {
	/**
	 * 类型名称转换实例<br>
	 * 输入原始类型名<br>
	 * 输出转换后的类型名
	 */
	public static Function<String, String> enumNameTransformer = null;
	public static Map<Class<? extends Enum<?>>,List<ExtensiveEnumItem>> beforeExtensiveEnumItems;
	public static Map<Class<? extends Enum<?>>,List<ExtensiveEnumItem>> afterExtensiveEnumItems;
    /** {@link DecoratorThriftEnumMetadata}缓存对象,
     * 保存每个{@link ThriftEnumMetadata}对应的{@link DecoratorThriftEnumMetadata}实例 
     */
    @SuppressWarnings({ "rawtypes" })
	private static final LoadingCache<ThriftEnumMetadata,DecoratorThriftEnumMetadata> 
    	ENUMS_CACHE = 
    		CacheBuilder.newBuilder().build(
    				new CacheLoader<ThriftEnumMetadata,DecoratorThriftEnumMetadata>(){
						@SuppressWarnings("unchecked")
						@Override
						public DecoratorThriftEnumMetadata load(ThriftEnumMetadata key) throws Exception {
							if(key.getEnumClass().getName().endsWith("ErpcProxyReturnCode")){
								System.out.println("ErpcProxyReturnCode");
							}
							Map<Class<? extends Enum<?>> ,List<ExtensiveEnumItem>> before = (Map<Class<? extends Enum<?>> , List<ExtensiveEnumItem>>) firstNonNull(beforeExtensiveEnumItems, Collections.emptyMap());
							Map<Class<? extends Enum<?>> ,List<ExtensiveEnumItem>> after = (Map<Class<? extends Enum<?>> , List<ExtensiveEnumItem>>) firstNonNull(afterExtensiveEnumItems, Collections.emptyMap());
							return new DecoratorThriftEnumMetadata(key)
									.setExtensiveItemsBefore(before.get(key.getEnumClass()))
									.setExtensiveItemAfter(after.get(key.getEnumClass()));
						}});
    /**  将{@link ThriftEnumMetadata}转换为 {@link DecoratorThriftEnumMetadata}对象 */
    @SuppressWarnings("rawtypes")
	public static final Function<ThriftEnumMetadata,ThriftEnumMetadata> 
    	ENUM_TRANSFORMER = new Function<ThriftEnumMetadata,ThriftEnumMetadata>(){
    		@Nullable
			@Override
			public ThriftEnumMetadata apply(@Nullable ThriftEnumMetadata input) {
				return null == input || input instanceof DecoratorThriftEnumMetadata
						? input
						: ENUMS_CACHE.getUnchecked(input);
			}};
	    private final ClassCommentProvider javadocCommentProvider ;
	private volatile ImmutableList<String> documentation;
	private List<ExtensiveEnumItem> extensiveItemsBefore = Lists.newArrayList();
	private List<ExtensiveEnumItem> extensiveItemsAfter =  Lists.newArrayList();
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private DecoratorThriftEnumMetadata(ThriftEnumMetadata input){
		super(input.getEnumName(),input.getEnumClass());
		javadocCommentProvider = javadocCommentProviderFactory != null 
				? javadocCommentProviderFactory.apply(getEnumClass())
				: null;
	}
	@Override
	public ImmutableList<String> getDocumentation() {
		// double checking
		if(documentation == null){
			synchronized (this) {
				if(documentation == null){
					documentation = super.getDocumentation();
					if( javadocCommentProvider != null){
						if(documentation == null || documentation.isEmpty()){
							documentation = javadocCommentProvider.commentOfClass();
						}
					}
				}
			}
		}
		return documentation;
	}

	@Override
	public Map<T, ImmutableList<String>> getElementsDocumentation() {
		Map<T, ImmutableList<String>> m = super.getElementsDocumentation();
		if(javadocCommentProvider != null){
	        ImmutableMap.Builder<T, ImmutableList<String>> elementDocs = ImmutableMap.builder();

			for (Entry<T, ImmutableList<String>> entry : m.entrySet()) {
				ImmutableList<String> value = entry.getValue();
				if(value.isEmpty()){
					value = javadocCommentProvider.commentOfField(entry.getKey().name());	
				}
				elementDocs.put(entry.getKey(), value);
			}
			return elementDocs.build();
		}
		return m;
	}
	@Override
	public String getEnumName() {
		return enumNameTransformer != null 
				? enumNameTransformer.apply(super.getEnumName()) 
				: super.getEnumName();
	}

	/**
	 * @return extensiveItemBefore
	 */
	public List<ExtensiveEnumItem> getExtensiveItemsBefore() {
		return extensiveItemsBefore;
	}
	/**
	 * @param extensiveItemBefore 要设置的 extensiveItemBefore
	 * @return 当前对象
	 */
	public DecoratorThriftEnumMetadata<T> setExtensiveItemsBefore(List<ExtensiveEnumItem> extensiveItemBefore) {
		if(extensiveItemBefore != null){
			this.extensiveItemsBefore.addAll(extensiveItemBefore);
			Collections.sort(this.extensiveItemsBefore);
			if( getByEnumConstant() != null){
				Integer first = getByEnumConstant().entrySet().iterator().next().getValue();
				List<ExtensiveEnumItem> reversed = Lists.reverse(this.extensiveItemsBefore);
				int last = first - 1;
				for(ExtensiveEnumItem ee:reversed){
					if(ee.constant == null){
						ee.constant = last --;
					}else{
						last = ee.constant - 1;
					}						
				}
			}
		}
		return this;
	}
	/**
	 * @return extensiveItemAfter
	 */
	public List<ExtensiveEnumItem> getExtensiveItemsAfter() {
		return extensiveItemsAfter;		
	}
	/**
	 * @param extensiveItemAfter 要设置的 extensiveItemAfter
	 * @return 当前对象
	 */
	public DecoratorThriftEnumMetadata<T> setExtensiveItemAfter(List<ExtensiveEnumItem> extensiveItemAfter) {
		if(extensiveItemAfter != null){
			this.extensiveItemsAfter = extensiveItemAfter;
		}
		return this;
	}


	public static class ExtensiveEnumItem implements Comparable<ExtensiveEnumItem>{
		public final String name;
		private Integer constant;
		public final ImmutableList<String> documentation;
		public ExtensiveEnumItem(String name, Integer constant, ImmutableList<String> documentation) {
			super();
			this.name = checkNotNull(name,"name is null");
			this.constant = constant;
			this.documentation = firstNonNull(documentation,ImmutableList.<String>of());
		}
		/**
		 * @return constant
		 */
		public Integer getConstant() {
			return constant;
		}
		/**
		 * @return name
		 */
		public String getName() {
			return name;
		}
		@Override
		public int compareTo(ExtensiveEnumItem o) {
			return name.compareTo(o.name);
		}
	}
}
